ASP.NET Core Web API 檔案上傳處理方式的實踐探討
TLDR
- 推薦採用「檔案上傳與業務邏輯分離」的架構:先上傳檔案取得 ID,再將 ID 隨業務資料提交。
- 避免在同一個 API 中混合處理檔案與業務資料,以維持 API 格式的一致性。
- 使用
[FromForm]處理檔案上傳,使用[FromBody]處理 JSON 業務資料,兩者應分開設計。 - 檔案下載建議整合至業務邏輯 Controller,以便實作細粒度的權限控管。
- 針對未完成業務綁定的暫存檔案,應建立排程任務定期清理,避免空間浪費。
開發模式與資料接收方式
在 ASP.NET Core 中,處理檔案上傳主要有兩種情境:
- 全端開發:通常使用 HTML 表單 Submit,後端透過
[FromForm]接收。 - 前後端分離:通常使用 AJAX 傳遞 JSON,後端透過
[FromBody]接收。
兩種主要處理方法的技術痛點
什麼情況下會遇到這個問題: 當開發者試圖在同一個 API 中同時處理檔案與複雜業務資料時。
使用表單 Submit (
[FromForm]):- 痛點:ASP.NET Core 對
[FromBody]與[FromForm]使用不同的ModelBinder機制。若要針對自訂型別處理,需同時實作ModelBinder與JsonConverter,導致維護成本增加。 - 影響:前後端協作時,若 API 突然從 JSON 改為 FormData,前端傳輸邏輯需大幅重構。
- 痛點:ASP.NET Core 對
使用 JSON 傳遞 Base64 字串 (
[FromBody]):- 痛點:資料傳輸量會增加約 1.33 倍。
- 影響:在大檔案傳輸時,可能引發效能瓶頸。
推薦方案:檔案上傳與業務資料提交分離設計
為了解決上述衝突,建議將檔案上傳與業務邏輯拆分為兩個獨立步驟。
實作流程
- 檔案上傳 API:前端使用
[FromForm]上傳檔案,後端儲存後回傳檔案 ID。 - 業務資料 API:前端在新增或修改業務資料時,僅傳遞檔案 ID。
- 資料管理:
- 業務資料主表儲存檔案 ID,或透過關聯表處理一對多關係。
- 建立排程任務,定期清理一天以上未被啟用的暫存檔案。
檔案資料驗證
若需在驗證階段檢查檔案資訊,可透過依賴注入 (DI) 在 ValidationAttribute 中取得服務:
csharp
protected override ValidationResult IsValid(
object value, ValidationContext validationContext
) {
using IServiceScope scope = validationContext.CreateScope();
IFileService service = scope.ServiceProvider.GetRequiredService<IFileService>();
// 執行驗證邏輯
}注意事項
檔案類型驗證(如副檔名)容易被竄改,建議僅作為初步過濾,核心安全檢查應包含檔案簽名識別或內容掃描。
延伸探討:檔案下載實作考量
檔案下載的實作方式取決於權限控管需求:
- 通用檔案下載 API:實作簡單,但難以針對特定業務場景實施細粒度權限檢查。
- 業務邏輯 Controller 下載:雖然實作較複雜,但能確保每個檔案下載都經過正確的業務權限驗證。
結論:建議將下載邏輯整合至業務 Controller,並將重複的下載功能封裝為共用服務 (Service),以兼顧權限安全性與程式碼複用性。
異動歷程
- 初版文件建立。
- 補上 GitHub 範例專案連結。